import type {ReactNode} from 'react'
import React, {useMemo} from 'react'
import {reactive} from './reactivity';
import {useOnce} from "./hook";
import {isElement} from "react-is";

/**
 * 通过slotName，从props或者props.children中获取插槽内容
 * @author  韦胜健
 * @date    2021/3/8 16:08
 */
function getSlot(slotName: string, fcProps: any): ReactNode {
    const {children} = fcProps
    if (null != children) {
        /*尝试从children里面取*/
        if (typeof children === "object") {
            if (!Array.isArray(children) && !isElement(children)) {
                /*如果children是一个对象，并且这个对象不是React元素，则这个对象为插槽对象，key为插槽名称*/
                if (children[slotName] != null) {
                    return children[slotName]
                }
            } else {
                /*children 是一个react元素或者数组*/
                if (slotName === 'default') return children
            }
        } else {
            /*children为简单数据类型，string、number或者函数*/
            if (slotName === 'default') {return children}
        }
    }
    if (null != fcProps[slotName]) {
        return fcProps[slotName]
    }
}

/**
 * slot的hook函数
 * @author  韦胜健
 * @date    2021/3/8 16:10
 */
export function useSetupSlots({fcPropsPersist, slots}: { fcPropsPersist: { current: any }, slots: string[] | undefined }) {
    if (!slots || slots.length === 0) {return {setupSlots: {}}}

    /*---------------------------------------slot exist-------------------------------------------*/

    /**
     * exist是一个响应式对象，并且对象引用一直不变
     * @author  韦胜健
     * @date    2021/3/8 16:11
     */
    const exist = useOnce(() => reactive((slots).reduce((prev, slotName) => (prev[slotName] = false, prev), {} as Record<string, boolean>)))
    /**
     * 生成每个slot是否存在的函数持久引用
     * @author  韦胜健
     * @date    2021/3/8 16:11
     */
    const isExist = useOnce(() => (slots).reduce((prev, slotName) => (prev[slotName] = () => exist[slotName], prev), {} as Record<string, () => boolean>))
    /**
     * 依赖，用于useMemo
     * @author  韦胜健
     * @date    2021/3/8 16:11
     */
    const fcSlots = slots.map(slotName => getSlot(slotName, fcPropsPersist.current))
    /**
     * 当slot变化，更新响应式对象exist
     * @author  韦胜健
     * @date    2021/3/8 16:12
     */
    useMemo(() => slots.forEach((slotName, index) => {
        const flag = null != fcSlots[index]
        if (exist[slotName] !== flag) { exist[slotName] = flag}
    }), fcSlots)

    /*---------------------------------------生成setupSlot（setup中使用的slot对象）-------------------------------------------*/

    const setupSlots = useMemo(() => slots.reduce((prev, slotName) => {
        prev[slotName] = Object.assign((defaultSlot: ReactNode) => {
            const passSlot = getSlot(slotName, fcPropsPersist.current)
            let slot = passSlot == null ? defaultSlot : passSlot
            return typeof slot === "function" ? slot() : slot
        }, {
            isExist: () => isExist[slotName]()
        })
        return prev
    }, {} as Record<string, any>), []) as any

    return {setupSlots}
}
